/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  BondKeyHashMap.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2020/05/12
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "BondKeyHashMap.h"

#include <core/strings/string_util.h>

namespace {
enum {
  kTypeLen = 3,      // 类型编码的长度
  kSubLen = 3,       // 子类型的编码长度
  kYearLen = 4,      // 年份长度
  kDictMax = 255,    // 小字典中能容纳的最大个数
  kInvalidHead = 0,  // 0表示bkey的head空值
  kSeqZeroMax = 2,   // 16bit中,最高位有多少个用于表示0字符
  kMaxBondId =
      1 << 22,  // 400w债券,这个值和kYearBase根bkey中的year占的位个数有关
  kYearBase = 2000,  // 年份最大支持范围2000-2032(1950+2^10))
  kYearMax = 2032  // 这个值和kYearBase根bkey中的year占的位个数有关
};
}
NameDictHash::NameDictHash(int capcity /*= 6*/, const char* str /*= nullptr*/)
    : m_count(0), m_capcity(1 << capcity), m_bitcount(capcity) {
  m_array = new NodeInfo*[m_capcity];
  memset(m_array, 0, m_capcity * sizeof(NodeInfo*));
}

NameDictHash::~NameDictHash() {
  for (int i = 0; i < m_capcity; i++) {
    NodeInfo* ptr = m_array[i];
    while (ptr) {
      NodeInfo* temp = ptr;
      ptr = ptr->next;
      delete temp;
    }
  }
}

void NameDictHash::Init(const char* str) {
  if (str) {
    std::vector<qb::base::strinfo> tokens;
    tokens.reserve(m_capcity);
    qb::base::StringUtil::SplitStringInfo(str, strlen(str), ",", tokens);
    for (int i = 0; i < (int)tokens.size(); i++) {
      if (tokens[i].len == 3) {
        Add(str + tokens[i].pos);
      }
    }
  }
}

int NameDictHash::Add(const char* str) {
  unsigned int hash = 5381;
  hash += (hash << 5) + (str[0]);
  hash += (hash << 5) + (str[1]);
  hash += (hash << 5) + (str[2]);
  hash = hash & 0x7FFFFFFF;

  // 避免除法,提升重大非常明显
  int pos = (hash >> m_bitcount) & (m_capcity - 1);  // hash%m_capcity;
  NodeInfo* head = m_array[pos];
  NodeInfo* ptr = head;
  CodeId ci(str);
  while (ptr) {
    if (ptr->code.id == ci.id) return ptr->index;
    ptr = ptr->next;
  }
  int index = m_count++;
  m_array[pos] = new NodeInfo(head, index, ci);
  return index;
}

int NameDictHash::Add(int id) {
  unsigned int hash = 0;
  int pos = (hash >> m_bitcount) & (m_capcity - 1);  // hash%m_capcity;
  int index = m_count++;
  m_array[pos] = new NodeInfo(m_array[pos], index, CodeId(id));
  return index;
}

BondKeyParser::BondKeyParser() : m_types(5), m_subs(6), m_markets(5) {
  const char* types = "ABS,CBB,COE,COR,CRM,FIN,GBB,GOV,INT,LGB,NCD,PDB";
  const char* subs =
      "AMP,BGB,CBB,CBS,CCB,CCP,CEB,CMN,COB,CRM,CSB,CSP,CVB,CXB,HJB,INT,LBS,LCP,"
      "LEB,LLB,LMN,LSP,MBS,NCB,OTB,PBB,PDB,PPN,PSB,RAB,SCB,SCV,SEB,SES,SSB,TET,"
      "TLB";
  const char* marks = ".CIB,.CNN,.NNC,.SSE,.SZE,.abb";
  m_types.Init(types);
  m_subs.Init(subs);
  m_markets.Init(marks);
  // 经过测试,对于20w的数据1w个插槽是合适的,效率和内存综合最佳
  // NameDict插入时间性能有待优化,改成哈希表可能更好
  // CacheLine是整块的内存,可以预先提前分配
  // bkey可以使用编码进一步减少尺寸
  // 参考F14Map,使用并行搜索,增大CacheLine尺寸会导致空间浪费
  // F14Map并行搜索:为每个节点生成一个1个字节的哈希值,将16个节点的哈希值凑成一个128位值,然后使用intel
  // sse指令 一次性搜索16个
  m_subs.Add(0);
}

bool BondKeyParser::Parse(const char* str, int len, ComBondKey& key) {
  /*
  bondkey编码规则
  A0000352020CORPPN0101.SSE
  J0001042016CORLEB0101.CIB

  ^([A-Z])(\d+)(20\d{2})([A-Z]+)(\d+)(\.\w+)$

  注意:编号末尾,还有少量债券会附加备注字符,备注可能有多个,备注后面可以有数字
  其中可选的备注有:
  SH(上交所)
  SZ(深交所)
  DSC(贴现)
  US(美元)
  SQS(上清所)

  A	类别		首个字符,后续是数字
  000035	ID		//纯数字,后面是年份
  2020	年份	//年份,以20开头,4个数字,之后是字母
  CORPPN	类型	//字母字符
  0101	编号	//数字
  .CIB	市场	以点开头

  首个字符是字母
  从1位置找到第一个字母字符及其范围
  看前面4个字符是数字,且符合年份19--或者20--
  后面若干个字符是数字,再后面是.开头的市场
  */
  int end = -1;
  if (!IsAlpha(str[0])) return false;
  int start = GetTypeRange(str, len, end);
  if (start < 0 || end < 0) return false;
  if (!(start + kTypeLen == end || start + kTypeLen + kSubLen == end))
    return false;
  // int year = ToInt<kYearLen>(str+start - kYearLen);
  int year = ToInt(str, start - kYearLen, kYearLen);
  if (year < kYearBase || year >= kYearMax) return false;

  int id = ToInt(str, 1, start - kYearLen - 1);
  if (start - kYearLen - 1 != 6 || id >= 65536)  // 发行人编号限制为6个数字
    return false;
  int seqlen = GetIntLength(str + end, len - end);
  if (seqlen <= 0) return false;

  // 16bit的seq拆分成两部分,高kSeqZeroMax表示0字符的个数,低位表示后面的整数值
  int seqend = end + seqlen;
  int nzero = 0;
  for (int i = end; i < seqend; i++) {
    if (str[i] == '0')
      nzero++;
    else
      break;
  }
  int seq = ToInt(str, end + nzero, seqlen);
  const int nzeromax = 1 << kSeqZeroMax;
  const int seqmax = 1 << (16 - kSeqZeroMax);
  if (nzero >= nzeromax || seq >= seqmax) return false;

  if (str[seqend] != '.' || len - seqend != 4) return false;

  int type = m_types.Add(str + start);
  if (type < 0) return false;

  int sub = 0;
  if (start + kTypeLen + kSubLen == end) {
    sub = m_subs.Add(str + start + kTypeLen);
    if (sub < 0) return false;
  }
  int market = m_markets.Add(str + seqend + 1);
  if (market < 0) return false;

  key.first.id = id;
  key.first.seq = seq;
  key.first.nzero = nzero;
  key.second.year = year - kYearBase;
  key.second.head = str[0];
  key.second.type = (char)type;
  key.second.sub = (start + kTypeLen + kSubLen == end) ? sub : 0;
  key.second.market = (char)market;
  key.SetValue(0);
  return true;
}

int BondKeyParser::GetIntLength(const char* str, int len) const {
  int i = 0;
  for (; i < len; i++) {
    if (str[i] < '0' || str[i] > '9') return i;
  }
  return i >= len ? len : (-1);
}

int BondKeyParser::GetTypeRange(const char* str, int len, int& end) {
  const int default_start = 11;  // 大多数债券的类型编码的起始位置,11
  int pos = 1;
  int start = -1;
  end = -1;
  if (default_start < len && IsAlpha(str[default_start]) &&
      !IsAlpha(str[default_start - 1])) {
    start = default_start;
    pos = start + 1;
  }
  for (; pos < len; pos++) {
    if (start < 0) {
      if (IsAlpha(str[pos])) start = pos;
    } else if (!IsAlpha(str[pos])) {
      end = pos;
      break;
    }
  }
  return start;
}

BondCodeParser::BondCodeParser() {
  const char* marks = ".IB,.SZ,.SH";
  m_markets.Init(marks);
}

bool BondCodeParser::Parse(const char* str, int len, BondCodeCompact& code) {
  if (!str || len <= 0) return false;
  // 000  数字  一个字母 数字 .字符串
  int num = 0, seq = 0;
  const char* ptr = str;
  int plen = len;
  int nzero = TestZero(ptr, plen);
  ptr += nzero;
  plen -= nzero;
  if (nzero > MaxZero) return false;
  int nnum = TestNum(ptr, plen, num);
  ptr += nnum;
  plen -= nnum;
  code.id = 0;
  code.index = 0;
  if (ptr[0] >= 'A' && ptr[0] <= 'Z') {
    ++ptr;
    ++plen;
    if (ptr[0] == '0') return false;
    int nseq = TestNum(ptr, plen, seq);
    ptr += nseq;
    plen -= nseq;
    if (seq > MaxSeq || num > MaxShortId) return false;
    code.first.id = num;
    code.first.code = 1 + ptr[0] - 'A';
    code.first.seq = seq;
    code.second.flag = 1;
  } else {
    if (nnum >= 12)  // 针对12个数字的做特殊检查
      return false;
    code.id = num;
  }
  if (plen != 3 || ptr[0] != '.') return false;
  code.second.zero = nzero;
  code.second.market = m_markets.Add(ptr);
  return true;
}
